/*
 * Project CelestialBeing: serialization.hpp
 * Created: 2023/12/14
 * This program uses MulanPSL2. See LICENSE for more.
 */

// Serializable objects are:
// 1. Instances of subclasses of Serializable.
// 2. PODs.
// 3. TWeakSerializablePtr and TSharedSerializablePtr.
// 3. std::string.
// 4. std::vector holding Serializable.
// 5. std::map holding Serializable.
// 6. std::array holding Serializable.
// Use Serialize and Deserialize to serialize and deserialize them.

// Extra serializable objects are:
// Anything that has declared Serialize and Deserialize functions.
// Use SerializeGeneral and DeserializeGeneral to serialize and deserialize them.
// Fall back to Serialize and Deserialize if they are just standard serializables.

// DO NOT SERIALIZE A POD WITHOUT SPECIAL TREATMENT FOR C POINTERS WITHIN.

#ifndef CELESTIALBEING_SERIALIZATION_HPP
#define CELESTIALBEING_SERIALIZATION_HPP

#include <iostream>
#include <vector>
#include <array>
#include "cb/common/celestial_being_base.hpp"
#include "base_meta.hpp"
#include "serializable.hpp"
#include "serializable_ptr.hpp"

CB_NAMESPACE_BEGIN

template<class T> class TWeakSerializablePtr;
template<class T> class TSharedSerializablePtr;

// Basic serializables: POD, serializable ptr, arrays of basic serializales
// Check if a type is basic serializable
template<typename T, typename = void> struct TIsBasicSerializable : std::false_type {};
// Serializable pointers are basic serializables.
template<typename T> struct TIsBasicSerializable<T, typename std::enable_if<TIsInstanceOfTemplate<T, TWeakSerializablePtr>::value, void>::type> : std::true_type {};
template<typename T> struct TIsBasicSerializable<T, typename std::enable_if<TIsInstanceOfTemplate<T, TSharedSerializablePtr>::value, void>::type> : std::true_type {};
// PODs are basic serializables (std::array excluded).
template<typename T> struct TIsBasicSerializable<T, typename std::enable_if<!TIsInstanceOfSTLArrayTemplate<T>::value && std::is_trivial_v<T>, void>::type> : std::true_type {};

// Check if a type is standard serializable
template<typename T, typename = void> struct TIsSerializable : std::false_type {};
// Basic serializables, subclasses of Serializable are standard serializables.
template<typename T> struct TIsSerializable<T, typename std::enable_if<TIsBasicSerializable<T>::value || std::is_base_of_v<Serializable, T>, void>::type> : std::true_type {};
// STL containers of standard serializables are standard serializables.
// 1st supported container: std::vector
template<typename T> struct TIsSerializable<T, typename std::enable_if<TIsInstanceOfTemplate<T, std::vector>::value && TIsSerializable<typename T::value_type>::value, void>::type> : std::true_type {};
// 2nd supported container: std::array
// std::array employs non-type template parameters, thus not supported by TIsInstanceOfTemplate
// Special treatment here.
template<typename T> struct TIsSerializable<T, typename std::enable_if<TIsInstanceOfSTLArrayTemplate<T>::value && TIsSerializable<typename T::value_type>::value, void>::type> : std::true_type {};
// 3rd supported container: std::map
template<typename T> struct TIsSerializable<T, typename std::enable_if<TIsInstanceOfTemplate<T, std::map>::value
                                                                       && TIsSerializable<typename T::mapped_type>::value && TIsSerializable<typename T::key_type>::value, void>::type> : std::true_type {};
// 4th supported container: std::string
template<typename T> struct TIsSerializable<T, typename std::enable_if<TIsInstanceOfTemplate<T, std::basic_string>::value
                                                                       && TIsSerializable<typename T::value_type>::value, void>::type> : std::true_type {};
// Arrays of serializables are serializables
template<typename T, size_t N> struct TIsSerializable<T[N], typename std::enable_if<TIsSerializable<T>::value>::type> : std::true_type {};

// Concept for standard serializable.
template<typename T> concept CSerializable = TIsSerializable<T>::value;
template<typename T> concept CSerializableSubclass = std::is_base_of_v<Serializable, T>;
template<typename T> concept CSerializablePtr = TIsSerializablePtr<T>::value;
// Exclude std::array
template<typename T> concept CSerializablePOD = std::is_trivial_v<T> && !TIsInstanceOfSTLArrayTemplate<T>::value;
template<typename T> concept CSerializableVector = TIsInstanceOfTemplate<T, std::vector>::value && CSerializable<typename T::value_type>;
template<typename T> concept CSerializableArray = std::is_array_v<T> && CSerializable<typename T::value_type>;
template<typename T> concept CSerializableSTLArray = TIsInstanceOfSTLArrayTemplate<T>::value && CSerializable<typename T::value_type>;
template<typename T> concept CSerializableMap = TIsInstanceOfTemplate<T, std::map>::value && CSerializable<typename T::mapped_type> && CSerializable<typename T::key_type>;
template<typename T> concept CSerializableString = TIsInstanceOfTemplate<T, std::basic_string>::value && CSerializable<typename T::value_type>;

// General serializable concepts.
template<typename T> concept CGeneralSerializableExtra = requires(const T t1, T t2, std::ostream & os, std::istream & is) {
    { t1.SerializeExtra(os) } -> std::same_as<void>;
    { t2.DeserializeExtra(is) } -> std::same_as<void>;
};

// Concept for general serializables (that are serializables or have declared serialize and deserialize functions).
template<typename T> concept CGeneralSerializable = CGeneralSerializableExtra<T> || CSerializable<T>;

template<CSerializableSubclass T> inline void Serialize (std::ostream & os, const T & t) {
    t.Serialize(os);
}
template<CSerializableSubclass T> inline void Deserialize (std::istream & is, T & t) {
    t.Deserialize(is);
}
template<CSerializablePtr T> inline void Serialize (std::ostream & os, const T & t) {
    t.Serialize(os);
}
template<CSerializablePtr T> inline void Deserialize (std::istream & is, T & t) {
    t.Deserialize(is);
}
template<CSerializablePOD T> inline void Serialize (std::ostream & os, const T & t) {
    os.write(reinterpret_cast<const char *>(&t), sizeof(t));
}
template<CSerializablePOD T> inline void Deserialize (std::istream & is, T & t) {
    is.read(reinterpret_cast<char *>(&t), sizeof(t));
}
template<CSerializableVector T> inline void Serialize (std::ostream & os, const T & t) {
    Serialize(os, t.size());
    for(const auto & item : t) {
        Serialize(os, item);
    }
}
template<CSerializableVector T> inline void Deserialize (std::istream & is, T & t) {
    size_t size;
    Deserialize(is, size);
    t.resize(size);
    for(auto & item : t) {
        Deserialize(is, item);
    }
}
template<CSerializableArray T> inline void Serialize (std::ostream & os, const T & t) {
    for(const auto & item : t) {
        Serialize(os, item);
    }
}
template<CSerializableArray T> inline void Deserialize (std::istream & is, const T & t) {
    for(auto & item : t) {
        Deserialize(is, item);
    }
}
template<CSerializableSTLArray T> inline void Serialize (std::ostream & os, const T & t) {
    size_t array_size = t.size();
    os.write(reinterpret_cast<const char *>(&array_size), sizeof(array_size));
    for(const auto & item : t) {
        Serialize(os, item);
    }
}
template<CSerializableSTLArray T> inline void Deserialize (std::istream & is, T & t) {
    size_t array_size;
    is.read(reinterpret_cast<char *>(&array_size), sizeof(array_size));
    assert(array_size == t.size());
    for(auto & item : t) {
        Deserialize(is, item);
    }
}
template<CSerializableMap T> inline void Serialize (std::ostream & os, const T & t) {
    Serialize(os, t.size());
    for(const auto & item : t) {
        Serialize(os, item.first);
        Serialize(os, item.second);
    }
}
template<CSerializableMap T> inline void Deserialize (std::istream & is, T & t) {
    size_t size;
    Deserialize(is, size);
    for(size_t i = 0; i < size; ++i) {
        typename T::key_type key;
        typename T::mapped_type value;
        Deserialize(is, key);
        Deserialize(is, value);
        t[key] = value;
    }
}
inline void Serialize (std::ostream & os, const std::string_view & t) {
    Serialize(os, t.size());
    os.write(reinterpret_cast<const char *>(t.data()), t.size() * sizeof(std::string_view::value_type));
}
template<CSerializableString T> inline void Serialize (std::ostream & os, const T & t) {
    Serialize(os, t.size());
    os.write(reinterpret_cast<const char *>(t.data()), t.size() * sizeof(typename T::value_type));
}
template<CSerializableString T> inline void Deserialize (std::istream & is, T & t) {
    size_t size;
    Deserialize(is, size);
    t.resize(size);
    is.read(reinterpret_cast<char *>(t.data()), size * sizeof(typename T::value_type));
}

// Serialize and deserialize for general serializables.
template<CSerializable T> inline void SerializeGeneral (std::ostream & os, const T & t) {
    Serialize(os, t);
}
template<CSerializable T> inline void DeserializeGeneral (std::istream & is, T & t) {
    Deserialize(is, t);
}

template<CGeneralSerializableExtra T> inline void SerializeGeneral (std::ostream & os, const T & t) {
    t.Serialize(os);
}
template<CGeneralSerializableExtra T> inline void DeserializeGeneral (std::istream & is, T & t) {
    t.Deserialize(is);
}

CB_NAMESPACE_END

#endif //CELESTIALBEING_SERIALIZATION_HPP
